package com.cat2bug.junit.service;

import com.cat2bug.junit.annotation.RandomParameter;
import com.cat2bug.junit.service.string.CharRandomStringService;
import javassist.ClassPool;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.*;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

/**
 * 参数服务类
 */
@Service
public class ParameterService {
	public final static String CHAR_RANDOM_STRING = "char";
	private Map<String, Class<?>> createParameterClassMap = new ConcurrentHashMap<>();

	public static ParameterService instance;

	private Map<String, IRandomStringService> randomStringServices = new HashMap<>();

	ClassPool pool = ClassPool.getDefault();

	private Random random;

	private ParameterService() {
		random = new Random();
		randomStringServices.put(CHAR_RANDOM_STRING, new CharRandomStringService());
	}

	public static ParameterService getInstance() {
		if (instance == null) {
			instance = new ParameterService();
		}
		return instance;
	}

	/**
	 * 添加参数创建类
	 * 
	 * @param caseClass 		可以创建参数值的类
	 * @param useClassNames    	使用创建类的其它类数组
	 * @throws Exception		异常
	 */
	public void addParameterCreateClass(Class<?> caseClass, String[] useClassNames) throws Exception {
		for (String cls : useClassNames) {
			this.createParameterClassMap.put(cls, caseClass);
		}
	}

	/**
	 * 获取测试用例方法
	 * @param srcClassName		源需要测试的类名称
	 * @param proxyClassName	封装代理的测试类名称
	 * @param srcMethodName		源方法名称
	 * @param parameterName		方法中的参数名
	 * @param paramType			方法中参数的类型
	 * @return					方法对象
	 * @throws Exception		异常
	 */
	public Method getTestCaseMethod(String srcClassName, String proxyClassName, String srcMethodName, String parameterName, String paramType)
			throws Exception {
		if (this.createParameterClassMap.containsKey(proxyClassName)) {
			Class<?> testCaseClass = this.createParameterClassMap.get(proxyClassName); // 获取创建类的实例
			// 遍历用户写的测试类中的方法，查找是否有定义随机参数的函数，如果有，就用用户函数计算参数值
			Method[] methods = testCaseClass.getMethods();
			for (Method m : methods) {
				RandomParameter rp = m.getAnnotation(RandomParameter.class);
				if (rp == null)
					continue;
				boolean isMatch = Pattern.matches(rp.className(), srcClassName);
				if (!isMatch && "".equals(rp.className()) == false)
					continue;
				isMatch = Pattern.matches(rp.methodName(), srcMethodName);
				if (!isMatch && "".equals(rp.methodName()) == false)
					continue;
				isMatch = Pattern.matches(rp.parameterName(), parameterName);
				if (!isMatch && "".equals(rp.parameterName()) == false)
					continue;
				if (this.typeEquals(paramType,m.getReturnType().getName()) == false) {
					continue;
				}
				return m;
			}
		}
		return null;
	}

	/**
	 * 判断两个类型是否相等
	 * @param t1	类型名1
	 * @param t2	类型名2
	 * @return		true为相等
	 */
	private boolean typeEquals(String t1,String t2) {
		try {
			return (t1.equals(t2) ||
					(Class.forName(t1).getSimpleName().toUpperCase().equals(t2.toUpperCase()) &&
							(t2.equals("long")||
							t2.equals("int")||
							t2.equals("char")||
							t2.equals("boolean")||
							t2.equals("double")||
							t2.equals("float")||
							t2.equals("short"))));
		} catch (ClassNotFoundException e) {
			return false;
		}
	}

	public static <T> Class<T> getFieldClass(Field field) throws ClassNotFoundException {
		Type genericType = field.getGenericType();
		if (genericType instanceof ParameterizedType) {
			ParameterizedType parameterizedType = (ParameterizedType) genericType;
			Type rawType = parameterizedType.getRawType();
			return (Class<T>)rawType;
		}
		return (Class<T>)field.getType();
	}

	/**
	 * 是否过滤掉指定类型的参数不处理
	 * @param typeName	类全名
	 * @return			是否过滤
	 */
	public boolean isFilter(String typeName) {
		return (typeName.equals(HttpServletResponse.class.getName())||
				typeName.equals(HttpServletRequest.class.getName()));
	}

	/**
	 * 获取字段范型
	 * @param field	字段类型
	 * @return		范型
	 * @param <T>	范型
	 */
	public static <T> Class<T> getFieldGenericClass(Field field) {
		Type genericType = field.getGenericType();
		if (genericType instanceof ParameterizedType) {
			ParameterizedType parameterizedType = (ParameterizedType) genericType;
			Type rawType = parameterizedType.getRawType();
			Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
			if (actualTypeArguments.length > 0 && actualTypeArguments[0] instanceof Class<?>) {
				return (Class<T>)actualTypeArguments[0];
			}
		}
		return null;
	}
	private void setClassFieldValue(int layer, Object obj) throws Exception {
			if(layer<0) return;
		Field[] fields = obj.getClass().getDeclaredFields();
		for (Field f : fields) {
			// 如果是静态或常量，就跳出
			if (Modifier.isStatic(f.getModifiers()) || Modifier.isFinal(f.getModifiers()))
				continue;
			Class<?> fieldClass = getFieldClass(f);
			Class<?> fieldGenericClass = getFieldGenericClass(f);
			Object value = createParameterRandomValue(layer, fieldClass, fieldGenericClass);
			if(value!=null) {
				try {
					f.setAccessible(true);
					f.set(obj, value);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}

	public Class stringToClass(String className) throws ClassNotFoundException {
		if (className.matches(".*(\\[\\])$")) {
			return Class.forName("[L"+className.substring(0,className.length()-2)+";");
		} else {
			switch (className) {
				case "boolean":
					return Boolean.class;
				case "int":
					return Integer.class;
				case "long":
					return Long.class;
				case "float":
					return Float.class;
				case "double":
					return Double.class;
				case "chat":
					return Character.class;
				default:
					return Class.forName(className);
			}
		}
	}

	public Object createParameterRandomValue(Class<?> clazz) throws Exception {
		return createParameterRandomValue(3, clazz);
	}

	public boolean isArray(Class<?> cls) {
		return cls.isArray();
	}

	public boolean isList(Class<?> cls) {
		return List.class.isAssignableFrom(cls);
	}

	public boolean isMap(Class<?> cls) {
		return Map.class.isAssignableFrom(cls);
	}

	public boolean isSet(Class<?> cls) {
		return Set.class.isAssignableFrom(cls);
	}

	public boolean isEnum(Class<?> cls) {
		return cls.isEnum();
	}

	public boolean isPrimitive(Class<?> cls) {
		return cls.isPrimitive() || cls.getName().startsWith("java.lang");
	}

	public boolean isDate(Class<?> cls) {
		return cls.equals(Date.class);
	}

	public Object createParameterRandomValue(int layer, Class<?> clazz, Class<?>... genericClass) throws Exception {
		if (isArray(clazz)) {
			return generateArrayData(clazz);
		} else if (isList(clazz)) {
			return generateListData(layer, clazz, genericClass);
		} else if (isMap(clazz)) {
			return generateMapData(clazz);
		} else if (isSet(clazz)) {
			return generateSetData(clazz);
		} else if (isEnum(clazz)) {
			return generateEnumData(clazz);
		} else if (isPrimitive(clazz)) {
			return generatePrimitiveData(clazz);
		} else if (isDate(clazz)) {
			return generateRandomDate();
		} else if (clazz.equals(Currency.class)) {
			return Currency.getInstance("USD"); // 或者随机选择一个货币
		} else {
			return generateCustomClassData(layer, clazz, genericClass);
		}
	}

	private Object generatePrimitiveData(Class<?> clazz) {
		if (clazz.equals(int.class) || clazz.equals(Integer.class)) {
			return random.nextInt();
		} else if (clazz.equals(double.class) || clazz.equals(Double.class)) {
			return random.nextDouble();
		} else if (clazz.equals(boolean.class) || clazz.equals(Boolean.class)) {
			return random.nextBoolean();
		} else if (clazz.equals(long.class) || clazz.equals(Long.class)) {
			return Math.round(Math.random()*Long.MAX_VALUE);
		} else if (clazz.equals(float.class) || clazz.equals(Float.class)) {
			return random.nextFloat();
		} else if (clazz.equals(short.class) || clazz.equals(Short.class)) {
			return (short) random.nextInt(Short.MAX_VALUE);
		} else if (clazz.equals(byte.class) || clazz.equals(Byte.class)) {
			return (byte) random.nextInt(Byte.MAX_VALUE);
		} else if (clazz.equals(char.class) || clazz.equals(Character.class)) {
			return (char) (random.nextInt(26) + 'a');
		} else if (clazz.equals(String.class)) {
			return randomStringServices.get(CHAR_RANDOM_STRING).getRandomString();
		}
		return null;
	}

	private Object generateArrayData(Class<?> clazz) throws Exception {
		Class<?> componentType = clazz.getComponentType();
		Object array = Array.newInstance(componentType, 5); // 生成长度为5的数组
		for (int i = 0; i < 5; i++) {
			Array.set(array, i, createParameterRandomValue(componentType));
		}
		return array;
	}

	private Object generateListData(int layer, Class<?> clazz, Class<?>... genericClass) throws Exception {
		List<Object> list = new ArrayList<>();
		ParameterizedType parameterizedType = (ParameterizedType) clazz.getGenericSuperclass();
		Class<?> elementType;
		if(genericClass.length>0) {
			elementType = genericClass[0];
		} else if(parameterizedType!=null && parameterizedType.getActualTypeArguments().length>0) {
			elementType = (Class<?>) parameterizedType.getActualTypeArguments()[0];
		} else {
			return list;
		}
		// TODO 如果List的范型对象也包含范型，暂时没有处理
		for (int i = 0; i < 5; i++) {
			list.add(createParameterRandomValue(layer, elementType));
		}
		return list;
	}

	private Object generateSetData(Class<?> clazz) throws Exception {
		Set<Object> set = new HashSet<>();
		ParameterizedType parameterizedType = (ParameterizedType) clazz.getGenericSuperclass();
		if(parameterizedType==null || parameterizedType.getActualTypeArguments().length==0){
			return set;
		}
		Class<?> keyType = (Class<?>) parameterizedType.getActualTypeArguments()[0];
		for (int i = 0; i < 5; i++) {
			set.add(createParameterRandomValue(keyType));
		}
		return set;
	}

	private Object generateMapData(Class<?> clazz) throws Exception {
		Map<Object, Object> map = new HashMap<>();
		ParameterizedType parameterizedType = (ParameterizedType) clazz.getGenericSuperclass();
		Class<?> keyType = (Class<?>) parameterizedType.getActualTypeArguments()[0];
		Class<?> valueType = (Class<?>) parameterizedType.getActualTypeArguments()[1];
		for (int i = 0; i < 5; i++) {
			map.put(createParameterRandomValue(keyType), createParameterRandomValue(valueType));
		}
		return map;
	}

	private Object generateEnumData(Class<?> clazz) throws Exception {
		Object[] enumConstants = clazz.getEnumConstants();
		int randomIndex = random.nextInt(enumConstants.length);
		return enumConstants[randomIndex];
	}

	private Date generateRandomDate() {
		long offset = Timestamp.valueOf("1970-01-01 00:00:00").getTime();
		long end = Timestamp.valueOf("2025-12-31 23:59:59").getTime();
		long diff = end - offset + 1;
		return new Date(offset + (long) (Math.random() * diff));
	}

	private Object generateCustomClassData(int layer, Class cls, Class<?>... genericClass) throws Exception {
		// TODO 自定义对象也会有范型，因此genericClass应该处理，暂时未处理
		try {
			// 获取构造函数
			Constructor<?> constructor = cls.getDeclaredConstructor();
			// 取消访问权限检查
			constructor.setAccessible(true);
			// 实例化类对象
			Object obj = constructor.newInstance();
			setClassFieldValue(--layer, obj);
			return obj;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
}
